home *** CD-ROM | disk | FTP | other *** search
Modula Implementation | 1994-09-22 | 7.1 KB | 329 lines |
- IMPLEMENTATION MODULE MathLib0;
- FROM Terminal IMPORT WriteString,WriteLn;
-
- (* Methods and approximations are taken from
- HART et al,
- Computer Approximations,
- SIAM Series in Applied Mathematics,
- John Wiley and Sons,
- New York, 1968
- *)
- TYPE RealCard = RECORD
- CASE : BOOLEAN OF
- FALSE: x : REAL |
- TRUE : y : CARDINAL
- END
- END;
-
- CONST RedMax = 16;
-
- VAR x,y,z : REAL;
- NegSign : BOOLEAN;
- shift : RECORD
- CASE : BOOLEAN OF
- TRUE: I : INTEGER |
- FALSE: C : CARDINAL
- END
- END;
- n : CARDINAL;
- Red : ARRAY [1..RedMax] OF REAL;
-
- PROCEDURE entier(x : REAL) : INTEGER;
- BEGIN
- NegSign:=x<0.0;
- x:=ABS(x);
- IF x>32767.0+FLOAT(ORD(NegSign)) THEN
- WriteLn;
- WriteString('Error: REAL too large in entier ');
- WriteLn;
- HALT;
- RETURN 0
- END;
- IF NegSign THEN
- RETURN -(TRUNC(-x)+1)
- ELSE
- RETURN TRUNC(x)
- END;
- END entier;
-
- PROCEDURE real(x : INTEGER) : REAL;
- BEGIN
- IF x<0 THEN
- RETURN -FLOAT(ABS(x))
- ELSE
- RETURN FLOAT(x)
- END
- END real;
-
-
- PROCEDURE ln(A : REAL) : REAL;
- VAR b : RealCard;
- (* Hart function 2701 *)
- BEGIN
- IF A<=0.0 THEN
- WriteLn;
- WriteString('Error: ln of value<=0.0 ');
- WriteLn;
- HALT;
- RETURN 0.0
- END;
- b.x:=A;
- shift.I:=b.y DIV 80H;
- DEC(shift.I,7EH); (*shift.I by power of 2*)
- b.y:= 3F00H+(b.y MOD 80H); (*b now in range 0.5..1.0*)
- z:=b.x;
- WHILE z<(1.0/1.4142135623) DO
- DEC(shift.I);
- z:=2.0*z;
- END;
- z:=(z-1.0)/(z+1.0);
- y:=z*z;
- y:=z*((0.89554061525)*y-3.31355617479)/(y-1.65677797691);
- y:= y+real(shift.I)*0.6931471805599453094172321;
- RETURN y;
- END ln;
-
- PROCEDURE exp(A : REAL) : REAL;
- (* Hart function 1800*)
- VAR b : RealCard;
- BEGIN
- IF A<0.0 THEN
- b.x:=-A;
- IF b.x>87.0 (*370.0*) THEN RETURN 0.0 END;
- NegSign:=TRUE;
- ELSE
- b.x:=A;
- IF A>87.0 (*370.0*) THEN
- WriteLn;
- WriteString('Error: value too large in exp');
- WriteLn;
- HALT;
- RETURN 0.0
- END;
- NegSign:=FALSE;
- END;
- shift.I:=b.y DIV 80H;
- DEC(shift.I,7BH); (*shift.I by power of 2*)
- IF shift.I>0 THEN
- b.y:=3D80H+(b.y MOD 80H) (*b now in range 0..1/32*)
- ELSE
- shift.I:=0
- END;
- y:=b.x*b.x;
- z:=b.x*(0.08332586539095234*y+5.000089649149271528);
- y:=1.0+(2.0*z)/(y+10.000179298301740601-z);
- FOR n:=shift.I TO 1 BY -1 DO
- y:=y*y;
- END;
- IF NegSign THEN y:=1.0/y END;
- RETURN y;
- END exp;
-
- PROCEDURE sqrt(A : REAL) : REAL;
- VAR b : RealCard;
- exp : CARDINAL;
- BEGIN
- IF A=0.0 THEN RETURN A END;
- IF A<0.0 THEN
- WriteLn;
- WriteString('Error: sqrt of value<0.0 ');
- WriteLn;
- HALT;
- RETURN 0.0
- END;
- b.x:=A;
- shift.I:=b.y DIV 80H;
- DEC(shift.I,7EH); (*shift.I by power of 2*)
- IF ODD(shift.I) THEN
- exp:=3E80H;
- INC(shift.I)
- ELSE
- exp:=3F00H
- END;
- b.y:=exp + b.y MOD 80H; (*b.x now in [0..1/2]*)
- x:=b.x;
- y:=((((2.97530391*x)+2.02772463)*x+1.09542405E-1)*x+3.16235E-4)/
- (((x+3.46399556)*x+6.41225367E-1)*x+9.408909E-3);
- (*this is the first guess at a result*)
- REPEAT
- z:=0.5*(x/y-y);
- y:=y+z;
- UNTIL ABS(z)<1.0E-7 (*-15*);
- (* now add size back in *)
- IF shift.I<>0 THEN
- b.x:=y;
- INC(b.y,(shift.C DIV 2)*80H);
- RETURN b.x
- ELSE
- RETURN y;
- END;
- END sqrt;
-
- PROCEDURE Reduce(VAR x :REAL);
- BEGIN
- IF x>Red[RedMax] THEN
- WriteLn;
- WriteString('Error: Too big argument in trig. function');
- WriteLn;
- HALT;
- x:=0.0;
- RETURN
- END;
- n:=1;
- WHILE x>Red[n] DO INC(n) END;
- IF n>1 THEN
- FOR n:=n-1 TO 1 BY -1 DO
- WHILE x>Red[n] DO x:=x-Red[n] END
- END
- END
- END Reduce;
-
- PROCEDURE sin(A : REAL) : REAL;
- BEGIN
- (* Hart function no 3362 *)
- IF A<0.0 THEN
- NegSign:=TRUE;
- x:=-A;
- ELSE
- NegSign:=FALSE;
- x:=A;
- END;
- Reduce(x);
- IF x>pi THEN
- NegSign:=NOT NegSign;
- x:=x-pi;
- END;
- IF x>(pi*0.5) THEN
- x:=pi-x;
- END;
- x:=(2.0/pi)*x;
- y:=x*x;
- y:= x*((10.42302058487*y-173.89497132272)*y+529.30152776255)/
- ((y+27.86575519992)*y+336.96381989527);
- IF NegSign THEN
- RETURN -y
- ELSE
- RETURN y
- END;
- END sin;
-
- PROCEDURE cos(A : REAL) : REAL;
- BEGIN
- RETURN sin(A+pi/2.0); (*can do better - eg Hart 3502*)
- END cos;
-
- PROCEDURE tan(A : REAL) : REAL;
- PROCEDURE Hart4282(A : REAL) : REAL;
- BEGIN
- x:=4.0/pi*A;
- y:=x*x;
- RETURN x*(-12.55329742424*y+212.42445758263)/
- ((y-71.59606050466)*y+270.46722349399);
- END Hart4282;
- BEGIN
- IF A<0.0 THEN
- x:=-A;
- NegSign:=TRUE
- ELSE
- x:=A;
- NegSign:=FALSE
- END;
- Reduce(x);
- IF x>pi THEN x:=x-pi END;
- IF x>(pi/2.0) THEN
- x:=pi-x;
- NegSign:=NOT NegSign;
- END;
- IF x>(pi/4.0) THEN
- z:=x;
- x:=(pi/2.0-x);
- IF x<1.0E-19 THEN
- y:=1.0E19
- ELSE
- z:=Hart4282(x);
- y:=1.0/z;
- END;
- ELSE
- y:=Hart4282(x)
- END;
- IF NegSign THEN
- RETURN -y
- ELSE
- RETURN y
- END;
- END tan;
-
- PROCEDURE arctan(A : REAL) : REAL;
- VAR NegSign:BOOLEAN;
- PROCEDURE Hart5071(x : REAL) : REAL;
- BEGIN
- y:=x*x;
- RETURN x*(6.3691887127*y+12.65998609915)/
- ((y+10.5891113168)*y+12.65998646243);
- END Hart5071;
- BEGIN
- IF A<0.0 THEN
- NegSign:=TRUE;
- A:=-A;
- ELSE
- NegSign:=FALSE;
- END;
- IF A>1.0E-4 THEN
- (* Reduce range to be in [0..tan(pi/8)]*)
- IF A=1.0 THEN
- A:=pi/4.0
- ELSIF A>1.0 THEN
- A:=pi/2.0-arctan(1.0/A)
- ELSIF A>=0.4142135623730950488 (*tan(pi/8)*) THEN
- A:=pi/4.0-Hart5071(2.0/(1.0+A)-1.0)
- ELSE
- A:=Hart5071(A);
- END;
- END;
- IF NegSign THEN
- RETURN -A
- ELSE
- RETURN A
- END;
- END arctan;
-
- PROCEDURE arctan2(Y, X : REAL) : REAL;
- VAR Quadrant :CARDINAL;
- BEGIN
- IF (X>=0.0) THEN
- IF Y>=0.0 THEN
- Quadrant:=1
- ELSE
- Y:=-Y;
- Quadrant:=4;
- END
- ELSE
- X:=-X;
- IF Y>=0.0 THEN
- Quadrant:=2
- ELSE
- Y:=-Y;
- Quadrant:=3;
- END;
- END;
- IF X<=1.0E-19*Y THEN
- x:=pi/2.0
- ELSE x:=arctan(Y/X) END;
- CASE Quadrant OF
- 1: RETURN x |
- 2: RETURN pi-x |
- 3: RETURN pi+x |
- 4: RETURN 2.0*pi-x
- END;
- END arctan2;
-
- BEGIN (* initialisation *)
-
- Red[1]:=2.0*pi;
- FOR n:=2 TO RedMax DO
- Red[n]:=3.0*Red[n-1]
- END
-
- END MathLib0.
-
-